#include <stdio.h>
#include <string.h>

#include "proxy.h"
#include "llhttp.h"

int load_config(int argc, char** argv, struct config_s* pconfig)
{
	strcpy(pconfig->proxy_addr, DEFAULT_PROXY_ADDR);
	pconfig->proxy_port = DEFAULT_PROXY_PORT;
	return 0;
}

void show_http_proxy(void)
{
	//print current proxy settings
	system("powershell -NoProfile -NonInteractive -Command \"Get-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'\
 | Select-Object ProxyEnable, ProxyServer, ProxyOverride\"");
}

/*
* set http proxy
* not work: only used for command tool like curl, not affect browser
* netsh command not work with same reason
set http_proxy=http://127.0.0.1:8080
set https_proxy=http://127.0.0.1:8080

check:
Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" | Select-Object ProxyEnable, ProxyServer, ProxyOverride

enable proxy:
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyEnable" -Value 1
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyServer" -Value "http://127.0.0.1:8080"
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyOverride" -Value "localhost;127.0.0.1"

disable proxy:
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyEnable" -Value 0
*/
char gcmd[2048];
void set_http_proxy(char* proxy_ip, unsigned short  port)
{
	sprintf(gcmd, "powershell -NoProfile -NonInteractive -Command \"\
Set-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' -Name \"ProxyEnable\" -Value 1; \
Set-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' -Name \"ProxyServer\" -Value \"%s:%d\"; \
Set-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' -Name \"ProxyOverride\" -Value \"localhost; 127.0.0.1\"  \
\"", proxy_ip, port);
	//printf("%s\n", gcmd);
	system(gcmd);
	//show_http_proxy();
}

void reset_http_proxy(void)
{
	system("powershell -NoProfile -NonInteractive -Command \"Set-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'\
 -Name \"ProxyEnable\" -Value 0 \"");
	//show_http_proxy();
}



/*
 * Update the value of the statistics. The update_level is defined in
 * stats.h
 */
int update_stats(status_t update_level)
{
	return 0;
}

#define STRING_LENGTH 1024
static char log_sstr[STRING_LENGTH];
/*
 * This routine logs messages to either the log file or the syslog function.
 */
void log_message(int level, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	vsnprintf(log_sstr, STRING_LENGTH, fmt, args);
	va_end(args);

	if(level <= glog_level)
		printf("%s\n", log_sstr);
}

/*
 * Add the error information to the conn structure.
 */
void indicate_http_error(struct conn_s* connptr, int number, const char* message, ...)
{
	va_list ap;
	va_start(ap, message);

	char* key, * val;
	while ((key = va_arg(ap, char*))) {
		val = va_arg(ap, char*);
		if (!strcmp(key, "detail")) {
			//only care detail info
			strncpy(connptr->detail_string, val, MAX_ERROR_MSG_LEN);
			break;
		}
	}

	connptr->error_number = number;
	vsnprintf(connptr->error_string, MAX_ERROR_MSG_LEN, message, ap);

	va_end(ap);
}


/*
 * Take a host string and if there is a username/password part, strip
 * it off.
 */
static void strip_username_password(char* host)
{
	char* p;

	//assert(host);
	//assert(strlen(host) > 0);

	if ((p = strchr(host, '@')) == NULL)
		return;

	/*
	 * Move the pointer past the "@" and then copy from that point
	 * until the NUL to the beginning of the host buffer.
	 */
	p++;
	while (*p)
		*host++ = *p++;
	*host = '\0';
}

/*
 * Take a host string and if there is a port part, strip
 * it off and set proper port variable i.e. for www.host.com:8001
 */
static int strip_return_port(char* host)
{
	char* ptr1;
	char* ptr2;
	int port;

	ptr1 = strrchr(host, ':');
	if (ptr1 == NULL)
		return 0;

	/* Check for IPv6 style literals */
	ptr2 = strchr(ptr1, ']');
	if (ptr2 != NULL)
		return 0;

	*ptr1++ = '\0';
	if (sscanf(ptr1, "%d", &port) != 1)    /* one conversion required */
		return 0;
	return port;
}



/*
 * Pull the information out of the URL line.
 * This expects urls with the initial '<proto>://'
 * part stripped and hence can handle http urls,
 * (proxied) ftp:// urls and https-requests that
 * come in without the proto:// part via CONNECT.
 */
int extract_url(const char* url, int default_port, struct request_s* request)
{
	char* p;
	int port;

	/* Split the URL on the slash to separate host from path */
	p = strchr(url, '/');
	if (p != NULL) {
		int len;
		len = (int)(p - url);
		request->host = (char*)safemalloc(len + 1);
		memcpy(request->host, url, len);
		request->host[len] = '\0';
		request->path = safestrdup(p);
	}
	else {
		request->host = safestrdup(url);
		request->path = safestrdup("/");
	}

	if (!request->host || !request->path)
		goto ERROR_EXIT;

	/* Remove the username/password if they're present */
	strip_username_password(request->host);

	/* Find a proper port in www.site.com:8001 URLs */
	port = strip_return_port(request->host);
	request->port = (port != 0) ? port : default_port;

	/* Remove any surrounding '[' and ']' from IPv6 literals */
	p = strrchr(request->host, ']');
	if (p && (*(request->host) == '[')) {
		memmove(request->host, request->host + 1,
			strlen(request->host) - 2);
		*p = '\0';
		p--;
		*p = '\0';
	}

	return 0;

ERROR_EXIT:
	if (request->host)
		safefree(request->host);
	if (request->path)
		safefree(request->path);

	return -1;
}

/*
 * Send a "message" to the file descriptor provided. This handles the
 * differences between the various implementations of vsnprintf. This code
 * was basically stolen from the snprintf() man page of Debian Linux
 * (although I did fix a memory leak. :)
 */
int write_message(struct conn_s* connptr, const char* fmt, ...)
{
	ssize_t n;
	size_t size = (1024 * 8);       /* start with 8 KB and go from there */
	char* buf, * tmpbuf;
	va_list ap;

	//write_message will call 
	connptr->cbuf.base = (char*)malloc(RW_BUF_SIZE);
	connptr->cbuf.len = RW_BUF_SIZE;
	buf = connptr->cbuf.base;

	while (1) {
		va_start(ap, fmt);
		n = vsnprintf(buf, size, fmt, ap);
		va_end(ap);

		/* If that worked, break out so we can send the buffer */
		if (n > -1 && (size_t)n < size)
			break;

		/* Else, try again with more space */
		if (n > -1)
			/* precisely what is needed (glibc2.1) */
			size = n + 1;
		else
			/* twice the old size (glibc2.0) */
			size *= 2;

		if ((tmpbuf = (char*)saferealloc(buf, size)) == NULL) {
			safefree(buf);
			return -1;
		}
		else
			buf = tmpbuf;
	}

	stream_write_client(&connptr->client_socket, n);
	return 0;
}

int send_http_headers(
	struct conn_s* connptr, int code,
	const char* message, const char* extra)
{
	const char headers[] =
		"HTTP/1.%u %d %s\r\n"
		"Server: %s/%s\r\n"
		"Content-Type: text/html\r\n"
		"%s"
		"Connection: close\r\n" "\r\n";

	return (write_message(connptr, headers,
		connptr->protocol.major != 1 ? 0 : connptr->protocol.minor,
		code, message, PACKAGE, VERSION,
		extra));
}


/*
 * Display an error to the client.
 */
int send_http_error_message(struct conn_s* connptr)
{
	const char* fallback_error =
		"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
		"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
		"\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
		"<html>\n"
		"<head><title>%d %s</title></head>\n"
		"<body>\n"
		"<h1>%s</h1>\n"
		"<p>%s</p>\n"
		"<hr />\n"
		"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n"
		"</html>\n";

	const char p_auth_str[] =
		"Proxy-Authenticate: Basic realm=\""
		PACKAGE_NAME "\"\r\n";

	const char w_auth_str[] =
		"WWW-Authenticate: Basic realm=\""
		PACKAGE_NAME "\"\r\n";

	/* according to rfc7235, the 407 error must be accompanied by
		   a Proxy-Authenticate header field. */
	const char* add = connptr->error_number == 407 ? p_auth_str :
		(connptr->error_number == 401 ? w_auth_str : "");

	send_http_headers(connptr, connptr->error_number,
		connptr->error_string, add);

	char* detail = connptr->detail_string;
	return (write_message(connptr, fallback_error,
		connptr->error_number,
		connptr->error_string,
		connptr->error_string,
		detail, PACKAGE, VERSION));
}
